home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
CD ROM Paradise Collection 4
/
CD ROM Paradise Collection 4 1995 Nov.iso
/
edit
/
aaem95ma.zip
/
CC.CC
< prev
next >
Wrap
C/C++ Source or Header
|
1995-03-06
|
43KB
|
847 lines
/* This file is CC.CC */
#include "em.h"
//#include <stdio.h>
//FILE*debug;
//#define DB 4
/*-----*/
#ifdef DB
static int depth(){long j,i;for(i=long(&i+2),j=0;i;i=*((long*)i),j++);return j;}
static void In(){int i=depth(),j; for(j=1;j<i;j++) putc(' ',debug);}
#endif
/*-----*//* fast compare s[0:n-1]==t[0:n-1] */
//static char str_cmp(void*s,void*t,int n){
//asm("pushl %esi"); asm("pushl %edi"); asm("cld");
//asm("movl 8(%ebp),%esi"); asm("movl 12(%ebp),%edi");
//asm("movl 16(%ebp),%ecx"); asm("repe"); asm("cmpsb"); asm("pushf");
//asm("popw %ax"); asm("andl $192,%eax");
//asm("xorl $64,%eax"); asm("popl %edi"); asm("popl %esi");}
/*-----*//* fast move s[0:n-1]=t[0:n-1] */
static void str_cpy(void*s,void*t,int n){
asm("pushl %esi"); asm("pushl %edi"); asm("cld");
asm("movl 8(%ebp),%edi"); asm("movl 12(%ebp),%esi");
asm("movl 16(%ebp),%ecx"); asm("rep"); asm("movsb"); asm("popl %edi");
asm("popl %esi");}
/*----- move M forwards 1, but skip certain char sequences; stop at whoa */
int rightC(mark&M,mark whoa){enum{endcomment=1,instring,incomment};
line*R=M.r; char*S=R->s; int c,C=M.c,z=0, n=R==whoa.r?(whoa.c<?R->n):R->n;
char A=C<n?S[C]:LF, B=C<n-1?S[C+1]:LF;
if(A=='/') if(B=='*') {C+=2; z=incomment; /* skip / * */
Z: if(C<n-1) if(S[C]=='*') if(S[C+1]=='/') {C+=2; z=0; goto E;}
if(C>=n) {if(R==whoa.r) goto E; /* skip * / */
C=0; if(!(R=R->next)) goto E; S=R->s; n=R==whoa.r?whoa.c:R->n;}
else ++C; goto Z;}
/* if(A=='/') if(B=='/') {C=n; goto E;} */ /* C++ // comment, skip to eol */
if(A=='\'') {if(++C>=n) goto E; /* character value */
if(S[C++]=='\\') {if(C>=n) goto E; /* \ found */
if(!((S[C]-'0')&~7)) { /* \nnn */
if(++C<n) if(!((S[C]-'0')&~7)) if(++C<n) if(!((S[C]-'0')&~7)) C++;}
else if(C<n) C++;}
if(C<n) if(S[C]=='\'') C++; goto E;}
if(A=='"') {C++; z=instring;
X: if((A=C<n?S[C]:LF)=='"') {C++; z=0; goto E;} /* string */
if(C>=n) {if(R==whoa.r) goto E; /* M.r=0 if runs off file */
C=0; if(!(R=R->next)) goto E; S=R->s; n=R==whoa.r?whoa.c:R->n;}
else C+=(A=='\\')?2:1; goto X;} /* in string, skip \ and char */
if(A=='*') if(B=='/') {C+=2; z=endcomment; goto E;} /* skip * / */
if(C<n) C++; else {if(R==whoa.r) goto E;
C=0; if(!(R=R->next)) goto E; /* S=R->s; */ n=R==whoa.r?whoa.c:R->n;}
E: M.c=C<?n; M.r=R; return z;} /* = whether now in string or comment */
/*----- move mark 1 char *//* if runs off file, leaves this->r==0 */
/* void mark::operator++(){if(++c>r->n) {c=0; r=r->next;}} */
/* void mark::operator--(){if(--c<0) if(r=r->prev) c=r->n; else c=0;} */
/*----- check C bracket matching in region R */
val bracketsC(const region&R){enum{endcomment=1,instring,incomment};
mark Q; static char *w,c,b[64],*X="([{}])"; int i,j,k=0,n=0;
for(Q=R.beg;!Q.r?0:Q!=R.end;j=rightC(Q,R.end)) {
if(!k) if(j==endcomment) {k=2; b[0]='*'; b[1]='/'; n=2;}
c=*Q; n<?=63;
if(w=index(X,c)) if(i=w-X,!n?0:i<3?0:b[n-1]+i==5) n--; else b[n++]=i;}
for(i=k;i<n;i++) b[i]=X[b[i]];
if(n>63?0:j==instring) b[n++]='"';
else if(n>62?0:j==incomment) {b[n++]='/'; b[n++]='*';}
b[n]=0; return val(b,n);}
/*-----*/
KF(cbrackets) {val b=bracketsC(B->Mark(N.i)-B->dot);
(B->Mark(N.i)-B->dot).color(White,Magenta);
if(!b.n) {Display="all brackets in region match"; return;}
pr(CW,"unpaired brackets in region: %s",b.s); Display=CW;}
/*-----*//* START OF DIRECTORY READER */
/* format of a directory entry returned by int21 ah=0x4e & ah=4f */
class FCBtime{public: uns short x;
void strcat(char*B){short hr=(x>>11)&31,min=(x>>5)&63,twosec=x&31;
pa(B,"%2d.%02d.%02d",hr,min,twosec*2);};};
class FCBdate{public: uns short x;
void strcat(char*B){static char*Month[]={"@","Jan","Feb","Mar","Apr","May",
"Jun","Jul","Aug","Sep","Oct","Nov","Dec",".",",",":"};
short year=(x>>9)&127,month=(x>>5)&15,day=x&31;
pa(B,"%4d %3s %2d",year+1980,Month[month],day);};};
class dirbuf{public:byte Reservebuf[21],attrib; FCBtime time; FCBdate date;
uns long size; char name[13];
void setDTA(){_ax=0x1a00; _dx=(uns int)this; int21();};/* DTA -> user var */
/*----- read first directory entry into DTA *//* 1 if file found, else 0 */
byte GetFirst(char*dirname, uns int attrib=0){
_ax=0x4e00; _cx=attrib; _dx=(uns int)dirname; int21(); return !_carry;};
/*----- read next directory entry into DTA *//* 1 if file found, else 0 */
byte GetNext(){_ax=0x4f00; int21(); return !_carry;}
int get(char*name){setDTA(); return GetFirst(name,~0);};};
dirbuf fileinfo;
unsigned long filesize(char*name){return fileinfo.get(name)?fileinfo.size:-1;}
class direntry{public:
byte attrib; FCBtime time; FCBdate date; uns long size; char name[13];
direntry*next;
direntry(dirbuf&d,direntry*n){reg char*s,*t,*u; attrib=d.attrib;size=d.size;
for(s=name,t=d.name,u=s+12;s<u;s++,t++) *s=to__lower[byte(*t)];
name[12]=0; date.x=d.date.x; time.x=d.time.x; next=n;};
/*----- information about an entry */
void print(char*B){pr(B,"%-15s%8u ",name,size);
date.strcat(B); strcat(B," "); time.strcat(B); if(attrib&31) strcat(B," (");
if(attrib&1) strcat(B,"readonly,"); if(attrib&2) strcat(B,"hidden,");
if(attrib&4) strcat(B,"system,"); if(attrib&8) strcat(B,"volname,");
if(attrib&16) strcat(B,"dir,"); if(attrib&31) B[strlen(B)-1]=')';};};
class directory{public:direntry**entry; int n; char*name;
directory(char*Name);
~directory(){int i;
for(i=0;i<n;i++) delete entry[i]; delete entry; delete name;};
Text copy();};
/*------*/
Text directory::copy(){int i,s;line*L[n+2]; for(i=n+1;i>=0;i--) L[i]=new line();
for(i=0;i<=n;i++) *L[i]-*L[i+1]; for(s=i=0;i<n;i++) s+=entry[i]->size;
pr(CW,"directory %s, has %1d entries, %1d bytes",name,n,s); *L[0]=val(CW);
for(i=0;i<n;i++) {entry[i]->print(CW); *L[i+1]=val(CW);}
return Text(L[0],L[n+1]);}
/*-----*/
KF(copydir){T1.getifn(Fn,"directory to list?"); setrek(T1,T1.copy());
directory D(T1.s); B->dot.yank(D.copy(),0);}
/*-----*/
/* int compare(void*S,void*T,int n){register uns char*s=S,*t=T,*u=s+n;
while(s<u) if(*s++-*t++) return s[-1]-t[-1]; return 0;} */
/*-----*/
enum {s_dont=0,s_name=1,s_size=2,s_date=3}; static char dirsorttype=s_name;
int ifswop(direntry*a,direntry*b){int i; switch(dirsorttype){
case s_dont: return 0;
case s_name: default: return strcmp(a->name,b->name)>0;
case s_size: return a->size>b->size;
case s_date: i=a->date.x-b->date.x; return i?i>0:(a->time.x>b->time.x);}}
/*----- read a directory */
directory::directory(char*N){name=new char[strlen(N)+1]; strcpy(name,N);
int attrib=~0; char *s,*t; int i,k,l; reg int j; direntry*ch=0; n=0;
dirbuf db; db.setDTA(); if(!db.GetFirst(N,attrib)) {entry=0; return;}
do if(db.name[0]!='.') {n++; ch=new direntry(db,ch);} while(db.GetNext());
reg direntry**e=new direntry*[n];
{reg direntry*dc; for(j=0,dc=ch;dc;j++,dc=dc->next) e[j]=dc;}
for(k=1,i=n-1;k;i--) for(k=j=0;j<i;j++) {reg direntry*a,**b=e+j; /* sort */
if(ifswop(b[0],b[1])) {a=b[0]; b[0]=b[1]; b[1]=a; k=1;}}
entry=e;}
/*-----*/
int samefile(reg char*s,reg char*t){reg int S=strlen(s),T=strlen(t);
return S>T ?0: strcmp(s,t+T-S) ?0: S==T ?1: t[T-S-1]=='\\';}
/*-----*/
struct {char x; int n; char*s;} dmh[16]={
{0, CR, " go to marked file or dir"},
{1, -ins, " new buffer in this dir"},
{1, -ctrl_insert," new subdir in this dir"},
{0, -pageup, " up a page"},
{0, -pagedown, " down a page"},
{0, -del, " delete marked file or dir"},
{1, -home, " go to parent dir"},
{1, -alt_K, " copy this dir list into a kill"},
{0, -alt_del, " drop marked file's buffer"},
{0, -alt_R, " rename marked file"},
{1, -2, " exit from this submenu"},
{0, 'N'-64, " sort by name"},
{0, 'S'-64, " sort by size"},
{0, 'D'-64, " sort by date"},
{0, 'X'-64, " don't sort"},
{1, -alt_end, " abort this subr call"}}; enum{dmhn=16};
/*-----*/
void filefromdirmenu(val&Dir,char*prompt,char*at/*=0*/){Text*W; directory*D=0;
char*kk,d[256],fn[13],*oldB=B->name; extern char*dirmenuhelp[]; val T; buffer*Z;
mousestate ms,MS; int c,Q,fl,i,I,j,k,n,s,v=gp_Rows-1,w=v-2,X=0,x=0,Y;
B->dotcc=-1; strcpy(d,Dir.s); c=strlen(d); if(c) if(d[c-1]=='\\') d[--c]=0;
if(at?strlen(at)>c:0) strncpy(fn,at+c+1,13); else fn[0]=0; j=0; ms=Jerry;
READDIC: strcpy(d+c,"\\*.*"); delete D; D=new directory(d); x=X; X=0;
YY: if(n=D->n) j%=n; else j=0; for(s=i=0;i<n;i++) s+=D->entry[i]->size;
Jerry.range(D->n?:1,4); Jerry.move(0,0);
if(fn[0]) if(!x?1:j<0) {j=0;
for(i=0;i<n;i++) if(samefile(D->entry[i]->name,fn)) break; if(i<n) j=i;}
j<?=n-1; d[c]=0; k=w/2; k=n<w?0:((j-w/4)/k)*k; k<?=n-w; k>?=0;
Y: pr(CW,"directory %s, %1d entries, %1d bytes (I was asking \"%s\")",
D->name,n,s,prompt); display(CW,0,0,Cyan); d[c]='\\';
for(i=k;i<n;i++) {if(i-k>=w) break; CW[1]=' '; D->entry[i]->print(CW+2);
strcpy(d+c+1,D->entry[i]->name);
CW[0]=!(Z=buf_named(d))?' ':Z->changed?'*':'o';
CW[gp_Cols]=0; display(CW,i-k+1,0,Orange);} d[c]=0;
display(n<2? "(ctrl-_ full help, home \032 parent directory, altend aborts)":
"(\030\031 select, ctrl-_ full help, home \032 parent dir, altend aborts, \
return chooses)", (i-k+1)>?1,0,Orange);
AA: i=k; k=w/2; k=n<w?0:((j-w/4)/k)*k; k<?=n-w; k>?=0;
if(i!=k) goto Y; Y=j-k+1; if(x!=-mousemove) Jerry.move(j,1);
if(n) scr(Y,1)=sch(2,White); Jerry.mc=1;
switch(x=getkey()){default: goto AA;
case -ins: strcat(d,"\\"); T.n=0; T.s=0; T.getifn(Fn,prompt,_buffer,d);
findbuf(T)->go_to(); setrek(T1,T.copy()); delete D; Jerry=ms; return;
case -ctrl_insert: strcat(d,"\\"); T.n=0; T.getifn(Fn,"new directory?",0,d);
display(" ",v,0); _ax=0x3900; _dx=(int)T.s; int21(); if(_carry) beep();
/** setrek(T1,T.copy()); */ X=1; goto READDIC;
case -alt_end: Jerry=ms; MOAN("aborted");
case -downarrow: if(n) j=(j+1 )%n; break;
case -uparrow: if(n) j=(j-1+n)%n; break;
case -pagedown: if(n) j=(j+w)<?(n-1); break;
case -pageup: if(n) j=(j-w)>?0; break;
case -mousemove: if(n) j=Jerry.y; break;
case -lbutton: case CR: Jerry.mc=0; if(!n) goto AA;
d[c++]='\\'; strcpy(d+c,D->entry[j]->name);
if(D->entry[j]->attrib&16) {
c=strlen(d); strcpy(d+c,"\\*.*"); directory*E=new directory(d);
delete D; D=E; refreshscreen(); fn[0]=0; j=0; goto YY;}
delete D; strcpy(Dir.s,d); Dir.n=strlen(d); Jerry=ms; return;
case -del: if(!n) goto AA; i=D->entry[j]->attrib&0x18;
if(i&0x16) goto DD; if(i) goto AA; /* volume name */
beep(); if(!yesno("do you really want to delete this PC file?")) goto AA;
beep(); i=yesno("do you REALLY want to delete this PC file?");
display(" ",v,0);if(!i) goto AA; d[c++]='\\'; strcpy(d+c,D->entry[j]->name);
_ax=0x4100; _dx=(int)d; int21(); d[--c]=0; X=1; goto READDIC;
DD: beep(); i=yesno("do you really want to delete this directory?");
display(" ",v,0);if(!i) goto AA; d[c]='\\'; strcpy(d+c+1,D->entry[j]->name);
_ax=0x3a00;_dx=(int)d; int21(); if(_carry)beep(); d[c]=0; X=1; goto READDIC;
case -home: delete D;
c=k=strlen(d); for(i=0;i<k;i++) if(d[i]=='\\') c=i; d[c]=0; refreshscreen();
if(c<k-1) strncpy(fn,d+c+1,13); else fn[0]=0; j=0; goto READDIC;
case -alt_K: setref(f,©dir); strcpy(d,!D?"D_isnull":D->name?:"(null)");
setrek(T1,copyof(d));
if(D) {nkill++; killring[nkill&=15].clear(); killring[nkill]=D->copy();}
j=0; goto READDIC;
case -alt_del: d[c]='\\'; strcpy(d+c+1,D->entry[j]->name); X=1;
Z=buf_named(d); d[c]=0; if(Z) if(deletebuffer(Z))goto READDIC; X=0; goto AA;
case -alt_R: d[c]='\\'; d[c+1]=T.n=0; T.s=0;
T.getifn(Fn,"rename this file as:",0,d); strcpy(d+c+1,D->entry[j]->name);
_ax=0x5600; _dx=(int)d; _di=(int)T.s; int21(); if(_carry) beep(); d[c]=0;
display(" ",v,0); X=1; goto READDIC;
case '_'-64: for(i=0;kk=dirmenuhelp[i];i++) display(kk,i+1,0,Cyan); X=1;
display("(type any char to continue)",i+1,0,Cyan); getkey(); goto READDIC;
case 'N'-64: dirsorttype=s_name; goto RESORT;
case 'S'-64: dirsorttype=s_size; goto RESORT;
case 'D'-64: dirsorttype=s_date; goto RESORT;
case 'X'-64: dirsorttype=s_dont;
RESORT: X=0; strcpy(fn,D->entry[j]->name); goto READDIC;
case -f1: case -rbutton: case -mbutton: Q=0;
for(i=0;i<dmhn;i++) display((n?:dmh[i].x)?dmh[i].s:" ",i+1,47,Cyan);
MS=Jerry; Jerry.move(0,0); Jerry.range(dmhn,4); i=0; scr(1,47)=1+256*White;
MENU: I=i; scr(i+1,47)=1+256*White; if(Q!=-mousemove) Jerry.move(i,0);
switch(Q=getkey()) {
case -uparrow: i=(i+dmhn-1)%dmhn; break;
case -downarrow: i=(i+1)%dmhn; break;
case -mousemove: i=Jerry.y; break;
case -mbutton: case -rbutton: i=10;
case CR: case -lbutton: if(n?:dmh[i].x) inject(dmh[i].n); else break;
Jerry=MS; for(i=0;i<dmhn;i++) display(" ",i+1,47,White); goto Y;}
if(I!=i) scr(I+1,47)=' '+256*White; goto MENU;}
if(n) scr(Y,1)=sch(' ',White); goto AA;}
/*-----*/
KF(gotodir){char*s; strcpy(s=Fn.s,B->name); int i,j=-1,n=Fn.n=strlen(Fn.s);
for(i=0;i<n;i++) if(s[i]=='\\') j=i;
if(j<1) MOAN("this buffer has no file name and thus no directory"); s[j]=0;
Fn.n=j; filefromdirmenu(Fn,"file?",B->name); findbuf(Fn)->go_to();}
/*-----*/
void bitpack(char*s,char*t,int n){int i; reg char*u;
for(i=0;i<n-7;i+=8) {u=t+i; *s++=
(u[0]<<7)+(u[1]<<6)+(u[2]<<5)+(u[3]<<4)+(u[4]<<3)+(u[5]<<2)+(u[6]<<1)+u[7];}
if(i==n) return; *s=0; for(;i<n;i++) *s=(*s<<1)|t[i];}
/*-----*/
void bitexp(reg char*t,reg char*s,int n) {reg char *a=t+n,c;
if(s) while(t<a) {c=*s++; *t++=c&1; *t++=(c&2)>>1; *t++=(c&4)>>2; *t++=(c&8)>>3;
*t++=(c&16)>>4; *t++=(c&32)>>5; *t++=(c&64)>>6; *t++=(c&128)>>7;}
else while(t<a) *t++=0;}
/*-----*/
void bitcopy(reg char*t,reg char*s,int n) {reg char *a=t+((n+7)>>3);
if(t) if(s) while(t<a) *t++=*s++; else while(t<a) *t++=0;}
/*-----*//* MY OWN FORMATTED PRINT */
/**** These print functions rely on C function arg values being stored at */
/* (sizeof(each arg's value) rounded up to a multiple of 4) byte intervals ****/
char*pr(char*B,char*F0,...) { return pr_(B, F0,(int*)(&F0)+1);}
char*pa(char*B,char*F0,...) { return pr_(B+strlen(B), F0,(int*)(&F0)+1);}
void pf(int f,char*F0,...) {if(f>=0) pr_((char*)f, F0,(int*)(&F0)+1);}
void pb(char*F0,...) { pr_((char*)_buffer,F0,(int*)(&F0)+1);}
void ps(char*F0,...) { pr_(0, F0,(int*)(&F0)+1);}
void p_r(char*B,char*F0,...) { pr_(B, F0,(int*)(&F0)+1);}
/* pr=string B, pa=string B append, pf=file f, pb=current buffer, ps=screen */
/*-----*/
char*pr_(char*S,char*F0,int*args){static char d[]="0123456789abcdef"; val*v;
char*XX=" format char\n"; char Buf[1024],*F=F0,*t,*u,*bb,*b,W[1024],s;
int c,m,n,p,q,r,rs,i,j,w,*P=args; uns long X,V; bb=b=(int)S>=4096?S:W;
/* Gnu C subr args are at 4*n-byte intervals */
while(c=*F++) {if(c!='%') {*b++=c; continue;} rs=s=w=0; p=1;
Z: switch (c=*F++) { /* w=wanted width, m=actual width, p=min width zerofill */
case 0: goto END;
case 'k': s=1;
case 'K': ((val*)(*P++))->expandkeyseq(b,s); b+=strlen(b); break;
case ' ': if(!s) s=' '; goto Z;
case '-': rs=1; goto Z;
case '+': s='+'; goto Z;
case '.': p=0; while(isdigit(c=*F++)) p=10*p+c-'0'; F--; goto Z;
case '0': p=-1; goto Z;
case'1':case'2':case'3':case'4':case'5':case'6':case'7':case'8':case'9':
w=c-'0'; while(isdigit(c=*F++)) w=10*w+c-'0'; F--; goto Z;
case 'c': t=(char*)(P++); n=1; goto STR; /****works as PC is little-endian*/
case 's': if(!(t=(char*)*P++)) t="(null)"; n=strlen(t); goto STR;
case 'S': if(!(t=(char*)*P++)) t="(null)"; n=strlen(t); goto TT;
case 't': n=strlen(t=keysort[-(int)*P++]); goto STR;
case 'T': u=b; b=(b+1)>?(bb+((int)*P++)); while(u<b) *u++=' '; break;
case 'v': v=(val*)*P++; t=v->s; n=t?v->n:0; if(v->magic()) goto MAGIC;
TT: *b++='"';
for(i=0;i<n;i++) {w=strlen(u=chname(t[i])); for(j=0;j<w;j++) *b++=u[j];}
*b++='"'; break;
MAGIC: n&=0x00ffffff; *b++='"';
for(i=0;i<n;i++) {if(bit(t+n+1,i)) *b++='`';
w=strlen(u=chname(t[i])); for(j=0;j<w;j++) *b++=u[j];}
*b++='"'; break;
case 'd': X=*P++; if((long)X<0) {X=-X; s='-';} r=10; goto NUM;
case 'o': X=*P++; r=8; goto NUM;
case 'u': X=*P++; r=10; goto NUM;
case 'x': X=*P++; r=16; goto NUM;
default: *b++=c;}; goto Y;
NUM: if(p<0) p=s?w-1:w; V=X;
m=0; if(V?:p) do m++; while(V/=r); m>?=p; /* m=#digits */ n=m+(s?1:0);
if(!rs) for(q=n;q<w;q++) *b++=' '; if(s) *b++=s;
for(c=m-1;c>=0;c--) {b[c]=d[X%r]; X/=r;} b+=m; goto J;
STR: if(!rs) for(q=n;q<w;q++) *b++=' '; u=t+n; while(t<u) *b++=*t++;
J: if(rs) for(q=n;q<w;q++) *b++=' '; Y:;}
END: *b=0;
if((int)S==_buffer) *B+=W; /* insert into current buffer */
else if(!S) wr(W); /* write to screen */
else if((int)S<4096) write((int)S,W,b-W); /* write on a file */ return b;}
/*-----*/
char**readtext(char*file){char*s; int F; if((F=open(file,0x8001))<0) return 0;
dirbuf d; if(!d.get(file)) return 0; int i,j,n=d.size; char*text=new char[n+1];
n=read(F,text,n); close(F); if(!n?:text[n-1]!=LF) text[n++]=LF;
for(i=j=0;i<n;i++) if(text[i]==LF) j++; char**lin=new char*[j+1]; lin[0]=text;
for(i=0,j=1;i<n;i++) if(text[i]==LF) {text[i]=0; lin[j++]=&text[i+1];}
for(s=lin[0]-2,i=1;i<j;i++) if(s[2]) if(*(s=lin[i]-2)==CR) *s=0; lin[j-1]=0;
return lin;}
/*-----*//* START OF SPELLING CHECKER */
typedef unsigned char byte; /* V=vowel, C=consonant, E=anything but 'e' */
static char*Buf,*P,*Be,conj[5]=" ai ",cw[1024],gems[]=" 23";
static int nconj=4,Wn=0,sn,D,pradd,alteredfrom,trace,pfstart,RV;
#define upb(array) (sizeof(array)/sizeof((array)[0]))
#define forways(m,w) for(m##e=(m=w.way)+w.wayn;m<m##e;m++)
enum{MAXDICT=200000,NWORDS=20000};
enum{noGem=0,Gem=1,GemUK=2};
enum{e_normal=0,e_keep=1,e_soften=2};
enum{supine=0,conjA=1,conjI=2,notlatin=3};
class Decl{public: uns int gem:2,cap:1,conj:2,e:2,bad:1;};
class Word{public: char*s; byte n; Decl decl;
inline char*suf(){return s+n+1;};
inline void operator=(val&v){s=v.s; n=v.n;};};
class Way {public: char*del,*add; byte deln,addn;};
class Suf {public: Way*way; byte wayn;};
static val*pre; static int npre; static Word word[NWORDS]; /******/
static Suf suf[256],desuf[32];
static int findword(val w);
static int addsuffix(char*word,int Wl,Decl decl,Way&m,char*saveend);
static byte _CV[32]=
{0,0,1,1,1,0,1,1,1,0,1,1,1,1,1,0,1,1,1,1,1,0,1,1,1,1,1,0,0,0,0,0};
inline int cons(char c){return _CV[c&31];}
inline int vowel(char c){return 1-_CV[c&31];}
#define forwords(w) for(w=sn;w<Wn;w++)
#define forall(w) for(w=1;w<Wn;w++)
#define forsuffs(s,w) for(s=w.suf();*s;s++)
/*-----*/
class wordlist{public: char**s; int n;
wordlist(int i=1){s=new char*[16]; s[0]=""; n=1;}; /* word 0 is dummy */
int place(char*w){int i,j,k,m; if(n<2) return -1; int wl=strlen(w);
for(j=0x8000,m=0;j>=1;j>>=1) if((k=m+j)<n) {
i=strncmp(w,s[k]+1,wl+1); if(!i) return k; if(i>0) m=k;}
return -m-1;};
inline int contains(char*w){return place(w)>0;};
int operator+(char*w){char**t; int i,j,k,m; int wl=strlen(w);
if((k=place(w))>0) return k; k=-k; t=s;
if(!(n&15)) {s=(char**)myalloc((n+16)*sizeof(char*));
for(i=0;i<k;i++) s[i]=t[i];}
for(i=n;i>k;i--) s[i]=t[i-1];
s[k]=(char*)myalloc(wl+2); str_cpy(s[k]+1,w,wl+1);
s[k][0]=0; if(s!=t) delete t; n++; return k;};
void empty(){int i; for(i=1;i<n;i++) delete s[i]; delete s; s=new char*[16];
s[0]=""; n=1;};
Text copy(int min){int i,j,m=0; for(i=1;i<n;i++) if(s[i][0]>=min) m++;
line*L[m+1]; for(i=0;i<=m;i++) L[i]=new line();
if(min>=0) {for(i=1,j=0;i<n;i++) if(s[i][0]>=min) {
*L[j]-*L[j+1]; *L[j++]=val(s[i]+1);}}
else for(i=1;i<n;i++) {*L[i-1]-*L[i]; strcpy(CW,s[i]+1); j=strlen(CW);
CW[j++]='/';CW[j++]='#'; CW[j++]='0'+s[i][0]; CW[j]=0; *L[i-1]=val(CW);}
return Text(L[0],L[m]);};};
wordlist appendix(1); int&appendixsize=appendix.n;
/*----- ONE-LINERS */
static void skipsp(){while(*P==' ') P++;}
static void findeol(){while(*P!='\n') P++; P++;}
static int nextis(char c){reg int j; skipsp(); if(j=*P==c) P++; return j;}
static int getn(){reg int n=0; while(isdigit(*P))n=n*10+*P++-'0'; return n;}
static void X(char*moan) {Wn=0;
pr(CW,"bad data at %dth byte of dictionary: %s",P-Buf,moan); MOAN(CW);}
/*-----*/
static char*getwword(byte&N){reg char c,*s=P,*t; reg int n=0; skipsp();
while(isalnum(c=*P)?1:c=='!'?:c=='+') {P++; n++;}
if(!(N=n)) return 0; t=new char[n+1]; str_cpy(t,s,n); t[n]=0; return t;}
/*-----*/
static val getheadword(){reg char *s; reg int n=0; skipsp(); s=P;
if(*P=='-'?:*P=='<') {n++; P++;} while(isalnum(*P)?:*P=='\'') {n++; P++;}
if(!n) return val(0,0); return val(s,n);}
/*-----*/
static void readways(Suf&S){Way way[32],*m; bzero(m=way,32*sizeof(Way));
do {if(m-way>=32) X("this suffix has > 32 methods");
m->del=getwword(m->deln)?:"";
if(nextis(':')) m->add=getwword(m->addn)?:"";
else {m->add=m->del; m->del=""; m->addn=m->deln; m->deln=0;}
m++;} while(nextis(','));
S.way=new Way[S.wayn=m-way]; memcpy(S.way,way,S.wayn*sizeof(Way));}
/*-----*/
static void setup(){static Way nullway={"","",0,0}; reg Way*m,*me; reg Word*W;
int i,j,k,l,n,s,w,wn; char *p,*q,T[256],TT[256],*Q[256+1]; byte sf[256];
bzero(word,sizeof(word)); Buf=new char[MAXDICT]; val DF(0,0),v;
suf[0].wayn=1; suf[0].way=&nullway; suf[1]=suf[0]; i=1;
strcpy(T2t.s,dictname); T2t.n=strlen(T2t.s);
DF.getifn(T2t,"dictionary file name?");
D=open(T2t.s,0x4001); /*read text*/ Display="reading dictionary"; showmoan();
Be=Buf+(n=read(D,Buf,MAXDICT)); if(n<=0) MOAN("I can't read the dictionary");
if(n==MAXDICT) MOAN("dictionary too big for buffer");
if(Be[-1]!='\n') *Be++='\n'; pradd=0;
for(P=Buf;strncmp(P,"--------",8);findeol()) if(P>=Be) X("bad dictionary");
findeol(); for(i=0;i<32;i++) {desuf[i].way=0; desuf[i].wayn=0;}
while(nextis('^')) {if(!isalpha(*P)) X("bad char after '^'"); i=(*P)&31; P+=2;
readways(desuf[i]); findeol();}
for(i=0,sn=1;;sn++) {findeol(); v=getheadword(); if(!v.n) X("bad headword");
Q[sn]=P; word[sn]=v; if(v.s[0]=='<') i++; else if(v.s[0]!='-') break;
if(sn>=256-1) X("too many suffixes");}
Wn=sn; pre=new val[npre=i]; RV=findword(val("-RV",3));
for(j=0,i=1;i<sn;i++) {reg Suf&S=suf[i]; P=Q[i]; S.wayn=0; S.way=0;
if(nextis('=')) readways(S); Q[i]=P;
if(word[i].s[0]=='<') pre[j++]=val(word[i].s+1,word[i].n-1);}
for(j=1;findeol(),P<Be;j++) {reg Word&W=word[j]; char*s; Word*V;
if(j>=NWORDS) X("too many entries");
if(j<=sn) P=Q[j]; else {v=getheadword(); if(!v.n) X("bad headword"); W=v;}
k=W.n;
if(j>1) if(strncmp(word[j-1].s,W.s,W.n+1)>=0) X("entry out of ascii order");
if(k>1)if(W.s[k-1]=='e')if(W.s[k-2]=='c'?1:W.s[k-2]=='g') W.decl.e=e_soften;
for(i=0;i<sn;i++) sf[i]=0;
while(nextis('/')) {skipsp(); /* stop at / \\ \n space */
for(s=P;*P=='/'?0:*P=='\\'?0:*P=='\n'?0:*P!=' ';P++);
i=(n=P-s)==1 ? *s : n==2 ? *s+256*s[1] : 0;
if(i=='2') W.decl.gem=Gem; /* doubles last letter */
else if(i=='3') W.decl.gem=GemUK; /* ditto, optional */
else if(i=='X') W.decl.bad=1; /* illegal word */
else if(*s=='#') ; /* start of marker used by putappendix */
else if(i=='!'+256*'a') W.decl.conj=conjA;
else if(i=='!'+256*'i') W.decl.conj=conjI;
else if(i=='!'+256*'n') W.decl.conj=notlatin;
else if(i=='K'+256*'E') W.decl.e=e_keep; /* -e stays before vowel */
else if(i=='P'+256*'N') W.decl.cap=1;
else if(i=='N'+256*'S') sf[0]=255; /* not self */
else if((*s=='-'?:*s=='<')?n>1:0) {k=1; if(s[n-1]=='*') {k=255; n--;}
if(!(l=findword(val(s,n)))) X("not a known suffix"); sf[l]=k;}
else X("rubbish after '/'");}
skipsp(); if(*P!='\n') X("rubbish after entry");
for(i=l=0;i<sn;i++) l+=(sf[i]<?2);
p=new char[W.n+1+l+1]; str_cpy(p,W.s,W.n); p[W.n]=0; q=p+W.n+1;
if(k=sf[0]) *q++=k; for(i=1;i<sn;i++) if(k=sf[i]) {*q++=i; if(k-1) *q++=k;}
*q=0; W.s=p;}
close(D); delete Buf; Wn=j;
for(pfstart=1;pfstart<sn;pfstart++) if(word[pfstart].s[0]=='<') break;
pr(CW,"dictionary read: %d words",Wn); Display=CW; showmoan();}
/*-----*/
static int addsuffix(val w,Decl decl,Way&m,char*saveend){
/* try a form of a suffix. Return 0 if this suffix won't work this way */
int i; reg char dn=m.deln,*s,*t; if(w.n-(w.s[0]=='-'?0:1)<dn) return 0;
#ifdef DB
debug=fopen("t$debug","a"); In();
fprintf(debug,"addsuffix(%s - %s + %s)\n",w.s,m.del,m.add); fclose(debug);
#endif
if(!dn) {saveend[0]=0; s=w.s+w.n;}
else {if(m.del[0]=='e') switch(decl.e) {
default: break;
case e_keep: return 0;
case e_soften: if(!index("iey",m.add[0])) return 0;}
for(s=w.s+w.n-dn,t=m.del;*t;t++,s++){
if(*t=='V') if(vowel(*s)) continue;
if(*t=='C') if(cons(*s)) continue;
if(*t!=*s) return 0;}
if(dn) str_cpy(saveend,s=w.s+w.n-dn,dn); saveend[dn]=0;}
if(t=m.add) if(*t) {
if(!dn) if(vowel(*t)?:*t=='y') if(decl.gem) {*s=s[-1]; s++;}
while(*t) if(*t=='!') switch(t++,decl.conj){
case conjA: *s++='a'; *s++='t'; break;
case conjI: *s++='i'; *s++='t'; default:;}
else if(*t=='+') {s++; t++;} else *s++=*t++;} *s=0;
alteredfrom=w.n-dn; return s-w.s;} /* return length of suffixed word */
/*-----*/
static int findword(val w){int i,j,k,l,n; if(!w.s) return 0;
for(j=0x8000,n=0;j>=1;j>>=1) if((k=n+j)<Wn) {/* word 0 in dictionary is dummy */
i=strncmp(w.s,word[k].s,w.n); if(!i) if(w.n<word[k].n) i=-1;
if(!i) return k; if(i>0) n=k;} return 0;}
/*-----*/
int checkderivs(val w,val u,Word&U,Decl decl){char*sf=U.suf(),rv; Word*Q;
#ifdef DB
debug=fopen("t$debug","a"); In();
fprintf(debug,"checkderivs(%s,%s as %s)",w.s,u.s,U.s);
int I; for(I=0;I<16;I++) fprintf(debug," %02x",byte(sf[I])); putc('\n',debug);
fclose(debug);
#endif
if(Breakin()) return 0; if(u.n>w.n+1) return 0; /* word has got too long */
int i,j,k,newun; Way*m,*me; byte s; if(byte(*sf)==255) sf++;
for(;s=*sf;sf++) {Word&W=word[s]; Suf&S=suf[s]; if(rv=(byte(sf[1])==255)) sf++;
#ifdef DB
debug=fopen("t$debug","a"); In();
fprintf(debug,"checkderivs(%s,%s as %s: %s)\n",w.s,u.s,U.s,W.s);
In(); fprintf(debug,"S.wayn=%1d\n",S.wayn);
fclose(debug);
#endif
if(W.s[0]=='<') {Way&P=S.way[0]; i=P.addn+u.n; char T[i+1],*s; val v(T,i);
str_cpy(T,P.add,P.addn); str_cpy(T+P.addn,u.s,u.n+1);
if(!strncmp(w.s,T,v.n+1)) return 1; s=W.suf();
if(byte(s[0])==255?!s[1]:0) {Word V; V.decl=U.decl; V.n=U.n; char*t;
char Y[512]; str_cpy(V.s=Y,U.s,i=U.n+1); t=Y+i; s=U.s+i;
while(*s) /* if(byte(*s)>=pfstart) break; else */ *t++=*s++; *t=0;
Q=&V;}
else Q=&W;
if(checkderivs(w,v,*Q,decl)) return 1;}
else if(!S.wayn) {if(checkderivs(w,u,W,decl)) return 1;}
else for(me=(m=S.way)+S.wayn;m<me;m++) {char T[16];
if(newun=addsuffix(u,decl,*m,T)) {
if(!strncmp(w.s,u.s,newun+1)) k=byte(*W.suf())!=255;
else k=checkderivs(w,val(u.s,newun),W,W.decl) ?: !rv?0:
checkderivs(w,val(u.s,newun),word[RV],W.decl);
strcpy(u.s+u.n-m->deln,T); if(k) return k; break;}}}
return 0; /* fault: no way to make the suffix wanted */}
/*-----*/
val Stak[64];
/*-----*/
static int checkwd(val w,val u,int D=0){if(D>=63?:Breakin()) return 0;
#ifdef DB
debug=fopen("t$debug","a"); In();
fprintf(debug,"checkwd(%s,%s)\n",w.s,u.s); fclose(debug);
#endif
int i,j,k,l,n; char T[512]; Way*m,*me; byte s; Suf*S; val*pr;
for(i=0;i<D;i++) if(!strncmp(u.s,Stak[i].s,u.n+1)) return 0; Stak[D]=u;
//for(j=i=0;i<D;i++) if(!strncmp(u.s,Stak[i].s,u.n+1)) j=i;
// if(j) {debug=fopen("t$debug","a"); In();
// fprintf(debug,"same as Stak[%d]\n",j); fclose(debug); return 0;}
//Stak[D]=u;
/* to stop a loop that developed e.g. ble -> blee -> ble -> etc */
for(j=0x8000,n=0;j>=1;j>>=1) if((k=n+j)<Wn) {Word&W=word[k];
i=strncmp(u.s,W.s,u.n+1); if(!i) if(u.n<W.n) i=-1;
if(!i) {if(W.decl.bad) return 0; /* word u found exactly */
if(!strncmp(w.s,u.s,u.n+1)) return (s=*W.suf())==255?0:k;
str_cpy(T,W.s,u.n+1); /* find if u can be extended to w */
if(checkderivs(w,val(T,u.n),W,W.decl)) return 1;
if(W.decl.gem==GemUK){Decl d=W.decl; d.gem=noGem; /* UK/USA variation */
if(checkderivs(w,val(T,u.n),W,d)) return 1;}
break;}
if(i>0) n=k;}
for(pr=pre+npre-1;pr>=pre;pr--) if(*u.s==*pr->s) if(!strncmp(u.s,pr->s,j=pr->n))
if(k=checkwd(w,val(u.s+j,u.n-j),D+1)) return k; /*try removing prefix from u*/
for(S=desuf+(u.s[u.n-1]&31),me=(m=S->way)+S->wayn;m<me;m++) {
if(u.n>m->deln) if(!strncmp(u.s+u.n-m->deln,m->del,m->deln)) {
if(u.n-m->deln+m->addn>511) break;
str_cpy(T,u.s,u.n+1); /* try removing suffix from u */
str_cpy(T+u.n-m->deln,m->add,m->addn+1); j=strlen(T);
if(m->addn>m->deln) if(j>2) if(T[j-1]==T[j-2]) if(T[j-2]==T[j-3]) break;
/* to stop a loop that developed: ble -> blee -> bleee -> etc */
if(k=checkwd(w,val(T,j),D+1)) return k;}}
if(u.n>2) if(cons(i=u.s[u.n-1])) if(i==u.s[u.n-2]) { /* doubled consonant */
str_cpy(T,u.s,u.n-1); T[j=u.n-1]=0;
k=checkwd(w,val(T,j),D+1); u.s[u.n++]=i; return k;}
return 0;}
/*-----*/
KF(checkspelling){char*eor="end of region"; int z=N.i?0:N.n; if(!N.n) N.i=0;
int i,j,k; mark X,Y=B->dot,Z=B->eof(); char *S,T[512]; B->dotcc=B->dotcc2=-1;
if(N.i) {Z=B->Mark(N.i); if(Z<Y) {X=Z; Z=Y; Y=X; setmark(N); B->dot=Y;}}
LOOP: X=Y; if(Breakin()) {B->dot=Y; Moan="user breakin"; return;}
if(X.is_alnum()) X.skip_alnum(1); else X.find_alnum(0,0);
if(X.eol()) {if(X.r==Z.r) {Display=eor; return;} ++X;
if(!X.r) {Moan="hit eof"; return;} Y=X; goto LOOP;}
Y=X; SW: Y.skipword(1); if(Y.r==Z.r) if(Y.c>Z.c) {Display=eor; return;}
B->dot=Y; B->display(); (X-Y).color(White,Magenta);
if(X.r!=Y.r) MOAN("bad craziness: end-of-line in word!");
if(X.c>=Y.c) MOAN("no word found"); if(Y.c-X.c>500) MOAN("word too long");
S=X.r->s+X.c; T[j=(Y.c-X.c)<?511]=0;
for(k=0,i=j;k<j;k++) if(isdigit(T[k]=to_lower(S[k]))) i--;
if(!Wn) setup(); i=!i ?: checkwd(val(T,j),val(T,j)) ?: appendix.contains(T);
if(!i) if(Y.c<Y.r->n-1) if(*Y=='\'') if(++Y,Y.is_alnum()) goto SW; else --Y;
if(i) if(!N.i) {Display="spelt correctly"; return;} else goto LOOP;
if(T1.n) {i=appendix+T; appendix.s[i][0]=1; goto LOOP;}
B->display(); B->dotcc=B->dotcc2=-1;
k=!N.i?1:yesno("misspelt or unknown word: shall I stop and let you change it?");
if(!k?:z) {
j=yesno("misspelt or unknown word: shall I list it as vocabulary?");
i=appendix+T; appendix.s[i][0]=j?1:0;} if(!k) goto LOOP;
(X-Y).color(White,Magenta); Moan="misspelt or unknown word";}
/*-----*/
KF(getappendix){checkspelling(N.n?N:val(1),val("y",1),val(0,0));}
/*-----*/
KF(putappendix){Text A=appendix.copy(N.i<?1); B->changed=1;
*(B->dot.r->prev?:&B->text)-*A.beg; *A.end-*B->dot.r; B->text.next->prev=0;
if(yesno("shall I also empty the dictionary's appendix?")) appendix.empty();}
/*-----*/
KF(emptyappendix){appendix.empty();}
/*-----*/
int bottommenu(
char*prompt,int nrb,char*codes,char**names,int L/*=8*/,int defolt/*=0*/){
mousestate ms; ms=Jerry; int c,j,k,m,n=strlen(codes),N=n*L,p; char*s,MN[L*n+1];
Jerry.range(4,n*L); Jerry.move(0,(k=defolt)*L); Jerry.mc=1; c=Jerry.bd=0;
for(c=0;c<L*n;c++) MN[c]=' '; p=strlen(prompt);
char ls[gp_Cols*2],*q=(char*)&scr(gp_Rows-1,0); str_cpy(ls,q,gp_Cols*2);
for(c=0;c<n;c++) for(j=strlen(s=names[c])-1;j>=0;j--) MN[L*c+j]=s[j]; MN[N]=0;
display(prompt,gp_Rows-1,0,Magenta); displayn(MN,gp_Rows-1,p,White+8,Blue);
M1: if(c!=-mousemove) Jerry.move(0,k); counterchange(scr(gp_Rows-1,k+p));
c=getkey(); if(c>='a') if(c<='z') c-=32; counterchange(scr(gp_Rows-1,k+p));
if(Jerry.buttons) {c=-mousemove; goto M1;} switch(c){
case -lbutton: case CR: m=k/L; goto RET;
case -mousemove: k=Jerry.x; goto M1;
case -leftarrow: k=(k-1)%N; goto M1;
case -rightarrow: k=(k+1)%N; goto M1;
case -mbutton: case -rbutton: m=nrb; goto RET;
default: for(m=0;m<n;m++) if(c==codes[m]) goto RET;} goto M1;
RET: Jerry=ms; str_cpy(q,ls,gp_Cols*2); return m;}
/*-----*/
KF(menu){int i,j,k,n,u,v; char*s,**z; uns short*p,*q,*r; subr*sb; mark MK;
uns short scrsave[gp_Cols*gp_Rows]; int ss=gp_Cols*gp_Rows*2;
typedef struct{char submenu; subr*sub; char*text;} menuline;
static menuline _bindmenu[]={{6,0,0},
{0,&bindkeybuf,"bind key to current buffer"},
{0,&bindkeymacro,"bind key to current macro"},
{0,&bindkeysubr,"bind key to subroutine"},
{0,&prbindings,"insert list of key bindings"},
{0,&prkeys,"insert list of keys & key bindings"},
{0,&unbindkey,"unbind key"}};
static menuline*bindmenu=_bindmenu;
static menuline _buf[]={{14,0,0},
{0,&buffermenu,"go to buffer by menu of buffers"},
{0,&gotodir,"go to file by menu of DOS directory"},
{0,&gotonbuf,"go to next buffer in chain"},
{0,©dir,"insert copy of DOS directory"},
{0,&delbuf,"remove current buffer (not its file)"},
{0,&insertfile,"insert text of a file"},
{0,&mergefile,"merge a file into region in ascii order."},
{0,&openfile,"go to file"},
{0,&prbuffers,"list buffers with bindings, in form obeyable by readmacros"},
{0,&renamebuffer,"change filename of buffer"},
{0,&restorebuf,"re-read buffer from its file"},
{0,&showinfo,"show information about buffer"},
{0,&writebuffer,"copy buffer to a file"},
{0,&savebuffer,"copy buffer to its file"}};
static menuline*buf=_buf;
static menuline _c[]={{3,0,0},
{0,&cbrackets,"check region for C bracket matching"},
{0,&cchrstr,"convert \\sequence to literal char"},
{0,&cstrchr,"convert char to \\sequence if needed in C string"}};
static menuline*c=_c;
static menuline _Case[]={{5,0,0},
{0,&capitalize,"capitalize word"},
{0,&lwcasewords,"lowercase word"},
{0,&upcasewords,"uppercase word"},
{0,&lwcaseregion,"lowercase region"},
{0,&upcaseregion,"uppercase region"}};
static menuline*Case=_Case;
static menuline _crlf[]={{3,0,0},
{0,&finalcr,"add CR marker in all lines in region"},
{0,&nofinalcr,"remove CR marker in all lines in region"},
{0,&linefeed,"insert LF without CR"}};
static menuline*crlf=_crlf;
static menuline _del[]={{13,0,0},
{0,&backspace,"delete char \033"},
{0,&delet,"delete char \032"},
{0,&killwordb,"kill word \033"},
{0,&killwordf,"kill word \032"},
{0,&killsent,"kill sentence \032"},
{0,&kill_to_eol,"kill (to) EOL"},
{0,&killregion,"kill region"},
{0,©kill,"copy region to kill ring"},
{0,&delblanklines,"delete blank lines round cursor"},
{0,&deletewhite,"delete all spaces & tabs round cursor"},
{0,&leaveonewhite,"delete blanks, leave 1 space"},
{0,&replyank,"replace yank by previous kill"},
{0,&yank,"yank last kill"}};
static menuline*del=_del;
static menuline _insert[]={{6,0,0},
{0,&insertspaces,"insert space"},
{0,&literalchar,"insert any char literally"},
{0,&newline,"insert EOL"},
{0,&openline,"insert EOL after cursor"},
{0,&insert,"insert text"},
{0,&overlay,"overlay text"}};
static menuline*insert=_insert;
static menuline _macro[]={{8,0,0},
{0,&beginmacro,"start recording macro"},
{0,&endmacro,"stop recording macro"},
{0,&namemacro,"give macro a name"},
{0,&obey,"obey current macro"},
{0,¯omenu,"menu of current macros"},
{0,&prmacro,"insert copy of current macro"},
{0,&prmacros,"insert copy of all macros"},
{0,&readmacros,"read macros & obey buffer bindings from a buffer"}};
static menuline*macro=_macro;
static menuline _mark[]={{7,0,0},
{0,&popmark,"popmark"},
{0,&pushmark,"pushmark"},
{0,&remmark,"remove top mark from stack"},
{0,&setmark,"set mark"},
{0,&swopmark,"swop mark with cursor"},
{0,&markbof,"set mark to start of buffer"},
{0,&markeof,"set mark to end of buffer"}};
static menuline*mark=_mark;
static menuline _misc[]={{20,0,0},
{0,&refresh_display,"cursor line becomes Nth/middle of screen"},
{0,&prkeynames,"insert list of names of all alt and special keys"},
{0,&prsubrnames,"insert list of all subroutine names"},
{0,&accentedletters,"stop/start treating PC accented letters as alphanumeric"},
{0,&calldos,"call DOS command"},
{0,&cccmode,"whether or not to counterchange at cursor"},
{0,&checkhelp,"insert report re any bound keys not listed in file HELP"},
{0,&dis_play,"display text on the mode line in cyan"},
{0,&displayhex,"display from cursor as hexadecimal"},
{0,&movetext,"move (text between mark N and mark N+1) to cursor"},
{0,©text,"copy (text between mark N and mark N+1) to cursor"},
{0,&repeat,"obey last instruction again"},
{0,&setrightmargin,"find/set right margin for auto eol"},
{0,&setsortcol,"find/set left margin for sortlines"},
{0,&showregion,"show region, or from mark 1 to mark 2"},
{0,&sortlines,"sort lines in region into alphabetical order"},
{0,×4,"multiply current N by 4"},
{0,&version,"display date of this version of AAEMACS"},
{0,&whoa,"discard arg typed"},
{0,&menu,"this subroutine (menu of subroutine descriptions)"}};
static menuline*misc=_misc;
static menuline _move[]={{20,0,0},
{0,&goleft,"\033 a char"},
{0,&goright,"\032 a char"},
{0,&gotocol,"go to Nth char in line"},
{0,&gouppart,"\030 a line-fold"},
{0,&godownpart,"\031 a line-fold"},
{0,&goup,"\030 a line"},
{0,&godown,"\031 a line"},
{0,&gotobol,"go to start of line"},
{0,&gotoeol,"go to end of line"},
{0,&gotoline,"go to line N"},
{0,&skipwordb,"\033 a word"},
{0,&skipwordf,"\032 a word"},
{0,&skipsentb,"\033 a sentence"},
{0,&skipsentf,"\032 a sentence"},
{0,&skipparab,"\030 a paragraph"},
{0,&skipparaf,"\031 a paragraph"},
{0,&page_down,"\031 a screenful"},
{0,&page_up,"\030 a screenful"},
{0,&gotobof,"go to top of buffer"},
{0,&gotoeof,"go to end of buffer"}};
static menuline*move=_move;
static menuline _reformat[]={{3,0,0},
{0,&formatpara,"reformat paragraph"},
{0,&formatregion,"reformat region"},
{0,&formatinsetregion,"reformat region and keep its left inset chars"}};
static menuline*reformat=_reformat;
static menuline _Repl[]={{4,0,0},
{0,&repl,"replace"},
{0,&replask,"replace, asking"},
{0,&replword,"replace, whole words only"},
{0,&replwordask,"replace, whole words only, asking"}};
static menuline*Repl=_Repl;
static menuline _Search[]={{6,0,0},
{0,&findb,"search \033"},
{0,&findf,"search \032"},
{0,&findwordb,"search \033, whole word only"},
{0,&findwordf,"search \032, whole word only"},
{0,&incrfindb,"incremental search \033"},
{0,&incrfindf,"incremental search \032"}};
static menuline*Search=_Search;
static menuline _spelling[]={{4,0,0},
{0,&checkspelling,"check spelling of next word or all region"},
{0,&emptyappendix,"empty the dictionary's appendix"},
{0,&getappendix,"load dictionary's appendix from region"},
{0,&putappendix,"copy dictionary's appendix into a buffer"}};
static menuline*spelling=_spelling;
static menuline _tab[]={{3,0,0},
{0,&expandtabs,"expand all tabs to spaces in region"},
{0,&maketabs,"convert spaces to tabs in region"},
{0,&tabulate,"insert or overlay tab char"}};
static menuline*tab=_tab;
static menuline _Twiddle[]={{3,0,0},
{0,&twiddle,"twiddle: move this char \032"},
{0,&twiddlewords,"twiddle: move this word \032"},
{0,&twiddlelines,"twiddle: move this line \031"}};
static menuline*Twiddle=_Twiddle;
static menuline _windows[]={{4,0,0},
{0,&splitwindow,"split this window"},
{0,&onewindow,"back to one-window mode"},
{0,&nextwindow,"go to next window in ring of windows"},
{0,&prevwindow,"go to previous window in ring of windows"}};
static menuline*windows=_windows;
static menuline _topmenu[]={{21,0,0},
{1,(subr*)bindmenu,"KEY BINDINGS"},
{1,(subr*)buf,"FILES AND BUFFERS"},
{1,(subr*)c,"C LANGUAGE FEATURES"},
{0,&callsubr,"call subr or macro"},
{1,(subr*)Case,"CASE OF LETTERS"},
{1,(subr*)del,"DELETING"},
{0,&help,"get help"},
{1,(subr*)insert,"INSERTING"},
{1,(subr*)macro,"MACROS"},
{1,(subr*)mark,"MARKS"},
{1,(subr*)misc,"MISCELLANEOUS"},
{0,&modemenu,"find & reset buffer's modes by a menu"},
{1,(subr*)move,"MOVE CURSOR"},
{1,(subr*)Twiddle,"MOVING A CHAR/WORD/LINE"},
{1,(subr*)reformat,"REFORMAT TEXT"},
{1,(subr*)Repl,"REPLACING"},
{1,(subr*)Search,"SEARCHING"},
{1,(subr*)spelling,"SPELLING CHECKER"},
{1,(subr*)tab,"TABULATING"},
{1,(subr*)windows,"SPLIT SCREEN MODE"},
{0,&finish,"exit from AAEMACS"}};
static menuline*topmenu=_topmenu,*M[4];
if(Jerry.mc) {char*MN[]={"Move","Copy","Delete","Exit","mainmenU"};
MK=B->Mark(1); (MK-B->Mark(2)).color(White,Magenta); B->display();
B->dotcc=B->dotcc2=-1; /* stop dotty() from playing with the cursor */
switch(bottommenu("",4,"MCDEU",MN)) {
case 0: movetext(); return;
case 1: copytext(); return;
case 2: (B->Mark(1)-B->Mark(2)).kill(); return;
case 3: return;
case 4:;}}
char mp[4]; mp[0]=0; M[0]=&topmenu[0]; int Z=0,m=0; val A(1,0); mousestate MS;
MS=Jerry; str_cpy(scrsave,screen,ss); thisstep.f=kf(_idle);
display("\030\031\033\032 to move pointer, RET to choose, END to back out,\
ctrl_ for help",gp_Rows-1,0,Cyan); Jerry.bd=0;
MENU: Jerry.mc=1; n=M[m][0].submenu; Jerry.range(n,80); Jerry.move(k=mp[m],0);
for(i=n*gp_Cols-1;i>=0;i--) screen[i]=256*White;
for(i=0;i<n;i++) {p=&screen[i*gp_Cols+1];
j=1; s=M[m][i+1].text; while(*s) *p++=(*s++)+256*Green;}
LINE: screen[k*gp_Cols]+=2;
CHAR: j=k; if(Z!=-mousemove) Jerry.move(k,0); switch(Z=getkey()){
case -mousemove: k=Jerry.y; break;
case -lbutton: goto CHOSEN;
case -uparrow: k--; break; case -downarrow: k++; break;
case -rightarrow: k+=5; break; case -leftarrow: k-=5; break;
case CR: goto CHOSEN;
case -end: case -mbutton: case -rbutton: Jerry.mc=0; goto BACKOUT;
case '_'-64: if(M[m][k+1].submenu) {beep(); goto CHAR;}
s=kf(M[m][k+1].sub).Subrname(); if(!(z=Subrhelp(s))) {beep(); goto CHAR;}
j=0; display(*z++,j++,0,Orange);
while(*z?**z!='{':0) display(*z++,j++,0,Orange); getkey(); goto MENU;
default: goto CHAR;}
while(k<0) k+=n; while(k>=n) k-=n; mp[m]=k;
if(j!=k) {screen[j*gp_Cols]&=0xff00; screen[k*gp_Cols]+=2;} goto CHAR;
BACKOUT: str_cpy(screen,scrsave,ss); if(m--) goto MENU; Jerry=MS; return;
CHOSEN: sb=M[m][k+1].sub;
if(M[m][k+1].submenu) if(m>=3) {MOAN("BUG: menus too deep");}
else{mp[m]=k;M[++m]=(menuline*)sb;str_cpy(screen,scrsave,ss);mp[m]=0;goto MENU;}
p=r=screen+(gp_Rows-1)*gp_Cols; for(i=0;i<gp_Cols;i++) p[i]=256*Orange;
s=kf(sb).Subrname(); while(*s) (*p++)+=*s++; Jerry.mc=0;
s=": numerical argument?"; while(*s) (*p++)+=*s++; q=p+1; u=1; v=0;
while(1) switch(i=getkey()) {
case -pagedown: case -lbutton: goto Z;
case '0'...'9': if(q<p+30) {v=v*10+i-'0'; *q++=i+256*Cyan;} else beep(); break;
case '+': u=1; *p=256*Cyan; break;
case '-': u=-1; *p='-'+256*Cyan; break;
case 8: if(q>p+1) {*--q=256*Cyan; v/=10;} break;
case -end: case -mbutton: case -rbutton: str_cpy(screen,scrsave,ss); goto MENU;
default: beep();}
Z: if(q>p+1) A=val(u*v); str_cpy(screen,scrsave,ss);
thisstep.f=kf(sb); thisstep.N=A; sb(A,val(0,0),val(0,0)); Jerry=MS;}